﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Sword.Data;
using Sword.Utility;

namespace Sword.ProxyCore.RuleParser
{
    public class TextRuleParser
    {
        static  Dictionary<string, Regex> regexMapping = new Dictionary<string, Regex>{
            {"comment", new Regex(@"^\s*#", RegexOptions.IgnoreCase)},
            {"host", new Regex(@"^\s*host\s*\:\s*", RegexOptions.IgnoreCase)},
            {"hostregex", new Regex(@"^\s*hostregex\s*\:\s*", RegexOptions.IgnoreCase)},
            {"url", new Regex(@"^\s*url\s*\:\s*", RegexOptions.IgnoreCase)},
            {"urlregex", new Regex(@"^\s*urlregex\s*\:\s*", RegexOptions.IgnoreCase)},
            {"header", new Regex(@"^\s*\S+\s*\:\s*", RegexOptions.IgnoreCase)},
        };

        static List<string> keyMatchOrder = new List<string>()
        {
            "comment","host","url","hostregex","urlregex"
        };
        static Dictionary<string, ConditionEnumType> ruleTypeMapping = new Dictionary<string, ConditionEnumType>{
            {"comment", ConditionEnumType.Comment},
            {"host", ConditionEnumType.Host},
            {"hostregex", ConditionEnumType.HostWithRegexExpression},
            {"url", ConditionEnumType.Url},
            {"urlregex", ConditionEnumType.UrlWithRegexExpression},
            {"header", ConditionEnumType.RequestHeader},
        };
        static List<string> resultTypes = new List<string>()
        {
            "host","ip","url","data","code"
        };
        static Dictionary<string, ResultEnumType> resultTypeMapping = new Dictionary<string, ResultEnumType>{
            {"host", ResultEnumType.Host},
            {"ip", ResultEnumType.IP},
            {"url", ResultEnumType.Url},
            {"data", ResultEnumType.Data},
            {"code", ResultEnumType.Code},
        };

        public static List<ConditionItem> Convert(AppConfigRootEntity config)
        {


            List<ConditionItem> rules = new List<ConditionItem>();
            foreach (var textRule in config.GroupInputRules)
            {
                var rule = ParserToConditionItem(textRule);
                if (rule != null && rule.ConditionType != ConditionEnumType.None && rule.ResultType != ResultEnumType.None)
                {
                    rules.Add(rule);
                }
            }

            return rules;
        }

        static ConditionItem ParserToConditionItem(string singleRule)
        {
            if(string.IsNullOrEmpty(singleRule) ||
               singleRule.Trim().StartsWith("#"))
            {
                return null;
            }
            ConditionItem item = new ConditionItem();

            item.ConditionType = JudgementRuleType(singleRule);

            var condition = ParserTwoTupleCondition(singleRule);
            if (condition != null)
            {
                item.Expression = condition.Item1;
                item.Result = condition.Item2;
            }

            item.ResultType = JudgementResultType(item.Result);
            item.ActualResult = CleanResultExpression(item.Result);

            return item;
        }

        static ConditionEnumType JudgementRuleType(string rule)
        {
            ConditionEnumType result = ConditionEnumType.None;
            foreach (string key in regexMapping.Keys)
            {
                if (!regexMapping.ContainsKey(key)) continue;

                if (regexMapping[key] == null) continue;

                if (Regex.IsMatch(rule, @""))
                    if (regexMapping[key].IsMatch(rule) && ruleTypeMapping.ContainsKey(key))
                    {
                        result = ruleTypeMapping[key];
                        break;
                    }
            }
            return result;
        }

        static ResultEnumType JudgementResultType(string resultExpression)
        {
            ResultEnumType result = ResultEnumType.None;
            foreach (string key in resultTypes)
            {
                if (Regex.IsMatch(resultExpression, @"^\s*" + key + @"\s*\:\s*", RegexOptions.IgnoreCase)
                    && resultTypeMapping.ContainsKey(key))
                {
                    result = resultTypeMapping[key];
                    break;
                }
            }
            if (result == ResultEnumType.None)
            {
                if (StringUtility.IsIp4Address(resultExpression.Trim()))
                {
                    result = ResultEnumType.IP;
                }
                else if (StringUtility.IsOnlyDomain(resultExpression.Trim()))
                {
                    result = ResultEnumType.Host;
                }
                else if (StringUtility.IsUrl(resultExpression.Trim()))
                {
                    result = ResultEnumType.Url;
                }
            }

            return result;
        }

        static Tuple<string, string> ParserTwoTupleCondition(string rule)
        {
            if (string.IsNullOrEmpty(rule))
            {
                return new Tuple<string, string>(string.Empty, string.Empty);
            }

            var regexClean = new Regex(@"^\s*((host)|(hostregex)|(url)|(urlregex))\s*\:\s*", RegexOptions.IgnoreCase);
            var regexSplit = new Regex(@"^(\S+)");
            var cleanRule = regexClean.Replace(rule, string.Empty);
            var ruleCodition = Regex.Split(cleanRule, @"^(\S+)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
            if (ruleCodition != null && ruleCodition.Length == 3)
            {
                return new Tuple<string, string>(ruleCodition[1].Trim(), ruleCodition[2].Trim());
            }

            return new Tuple<string, string>(string.Empty, string.Empty);
        }

        static string CleanResultExpression(string resultExpression)
        {
            if (string.IsNullOrEmpty(resultExpression))
            {
                return string.Empty;
            }

            Regex replacer = new Regex(@"^(url:)|(host:)|(ip:)|(data:)");
            var result = replacer.Replace(resultExpression, string.Empty);
            if (result.StartsWith("//"))
            {
                result = "http:" + result;
            }
            else if (result.StartsWith("http//"))
            {
                result = result.Replace("http//", "http://");
            }
            else if (result.StartsWith("https//"))
            {
                result = result.Replace("https//", "https://");
            }

            return result;
        }
    }
}
